package com.hh.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hh.common.ErrorCode;
import com.hh.constant.UserConstant;
import com.hh.entity.domain.User;
import com.hh.entity.dto.user.UserQueryRequest;
import com.hh.entity.dto.user.UserUpdatePwdRequest;
import com.hh.entity.dto.user.UserUpdateRequest;
import com.hh.entity.enums.UserRoleEnum;
import com.hh.entity.vo.LoginUserVO;
import com.hh.entity.vo.UserVO;
import com.hh.exception.BusinessException;
import com.hh.mapper.UserMapper;
import com.hh.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.hh.constant.SignInConstant.buildSignKey;
import static com.hh.constant.SignInConstant.getUserSignInRedisKey;

/**
 * @author hanhui
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2025-02-20 11:07:04
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {


    @Resource
    private UserMapper userMapper;
    @Resource
    private StringRedisTemplate StringRedisTemplate;

    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {
        //校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号长度过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度过短");
        }
        Pattern pattern = Pattern.compile("^[a-zA-Z0-9_]+$");
        //userAccount被检测的文本
        Matcher matcher = pattern.matcher(userAccount);
        //匹配上的时候返回true,匹配不通过返回false
        boolean matches = matcher.matches();
        if (!matches) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //密码和校验码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //账户不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        long count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.USER_ALREADY_EXISTS);
        }
        //2.加密
        String encryptPassword = getEncryptPassword(userPassword);
        //3.插入数据
        User user = new User();
        user.setUserStatus(1);
        user.setUserRole(UserRoleEnum.USER.getValue());
        user.setUserAccount(userAccount);
        user.setUserName("默认用户");
        user.setUserPassword(encryptPassword);
        user.setGender(0);
        user.setAvatarUrl("https://img.wxcha.com/m00/96/64/1f1a08f78ab923dd6c63d48a102a4ec0.jpg?down");
        boolean saveResult = this.save(user);
        if (!saveResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        return user.getId();
    }

    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        //校验

        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号长度过短");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度过短");
        }
        Pattern pattern = Pattern.compile("^[a-zA-Z0-9_]+$");
        //userAccount被检测的文本
        Matcher matcher = pattern.matcher(userAccount);
        //匹配上的时候返回true,匹配不通过返回false
        boolean matches = matcher.matches();
        if (!matches) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2.加密
        String encryptPassword = getEncryptPassword(userPassword);

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.USER_NOT_FOUND);
        }
        log.info("userLogin success, user:{}", user.toString());
        Integer userStatus = user.getUserStatus();
        if (userStatus == null || userStatus == 0) {
            throw new BusinessException(ErrorCode.ACCOUNT_DISABLE);
        }
        request.getSession().setAttribute(UserConstant.USER_LOGIN_KEY_PREFIX, user);
        return this.getLoginUserVO(user);
    }

    /**
     * 登录时，获取脱敏用户信息
     *
     * @param user 用户
     * @return 脱敏用户信息
     */
    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVo = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVo);
        return loginUserVo;
    }

    @Override
    public boolean userLogout(HttpServletRequest request) {
        User loginUser = this.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        request.getSession().removeAttribute(UserConstant.USER_LOGIN_KEY_PREFIX);
        return true;
    }

    /**
     * 获取脱敏用户信息
     *
     * @param user
     * @return
     */
    @Override
    public UserVO getUserVo(User user) {
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        UserVO userVo = new UserVO();
        BeanUtils.copyProperties(user, userVo);
        return userVo;
    }

    /**
     * 获取脱敏用户列表
     *
     * @param userList 用户列表
     * @return 脱敏用户列表
     */
    @Override
    public List<UserVO> getUserVoList(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream()
                .map(this::getUserVo)
                .collect(Collectors.toList());
    }

    /**
     * 更新用户状态
     *
     * @param userUpdateRequest 用户更新请求
     * @return
     */
    @Override
    public boolean updateUserStatus(UserUpdateRequest userUpdateRequest) {
        if (userUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = new User();
        BeanUtils.copyProperties(userUpdateRequest, user);
        return this.updateById(user);
    }

    /**
     * @param userUpdatePwdRequest 用户密码更新请求
     * @return UserVO
     */
    @Override
    public UserVO updatePassword(UserUpdatePwdRequest userUpdatePwdRequest, HttpServletRequest request) {
        // 1. 参数校验
        if (userUpdatePwdRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long loginUserId = getLoginUser(request).getId();
        Long userId = userUpdatePwdRequest.getUserId();
        if (!loginUserId.equals(userId)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "只能修改自己的密码");
        }
        String oldPassword = userUpdatePwdRequest.getOldPassword();
        String newPassword = userUpdatePwdRequest.getNewPassword();
        String confirmPassword = userUpdatePwdRequest.getConfirmPassword();
        // 2. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.USER_NOT_FOUND, "用户不存在");
        }
        // 3. 校验旧密码
        String encryptedOldPassword = getEncryptPassword(oldPassword);
        if (!encryptedOldPassword.equals(user.getUserPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "旧密码错误");
        }
        if (!newPassword.equals(confirmPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "新密码与确认密码不一致");
        }
        // 4. 更新密码
        String encryptedNewPassword = getEncryptPassword(newPassword);
        user.setUserPassword(encryptedNewPassword);
        boolean updateResult = this.updateById(user);
        if (!updateResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码更新失败");
        }
        // 5. 返回更新后的用户信息
        return getUserVo(user);
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return User
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_KEY_PREFIX);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            log.error("当前登录用户为空");
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return this.getById(currentUser.getId());
    }

    /**
     * 获取加密密码
     *
     * @param userPassword 密码
     * @return 加密密码
     */
    @Override
    public String getEncryptPassword(String userPassword) {
        return DigestUtils.md5DigestAsHex((UserConstant.SALT + userPassword).getBytes());
    }

    /**
     * 获取查询条件
     *
     * @param userQueryRequest 用户查询请求
     * @return 查询条件
     */
    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {

        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String userName = userQueryRequest.getUserName();
        String userAccount = userQueryRequest.getUserAccount();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ObjUtil.isNotNull(id), "id", id);
        queryWrapper.like(StrUtil.isNotBlank(userName), "userName", userName);
        queryWrapper.like(StrUtil.isNotBlank(userAccount), "userAccount", userAccount);
        queryWrapper.orderBy(StrUtil.isNotBlank(sortField),
                sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }

    /**
     * 是否为管理员
     *
     * @param user 用户
     * @return 是否为管理员
     */
    @Override
    public boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 添加用户签到记录
     *
     * @param userId 用户签到
     * @return 当前是否已签到成功
     */
    @Override
    public boolean addUserSignIn(long userId) {
        LocalDate date = LocalDate.now();
        String key = getUserSignInRedisKey(date.getYear(), userId);
        int day = date.getDayOfMonth();
        //检查今天是否签到
        Boolean isSignIn = StringRedisTemplate.opsForValue().getBit(key, day - 1);
        if (isSignIn != null && isSignIn) {
            return false;//今天已经签到过了
        }
        //        // 添加签到记录
        StringRedisTemplate.opsForValue().setBit(
                key, day - 1, true);
        //设置过期时间
        StringRedisTemplate.expire(key, 100, TimeUnit.DAYS);
        //连续签到记录
        updateContinuousSignDays(userId);
        return true;
    }

    /**
     * 判断用户指定日期是否签到
     */
    public boolean isSignedIn(Long userId, LocalDate date) {
        int day = date.getDayOfMonth();
        String key = buildSignKey(userId, date);

        Boolean isSigned = StringRedisTemplate.opsForValue().getBit(key, day - 1);
        return isSigned != null && isSigned;
    }

    /**
     * 更新连续签到天数
     */
    public void updateContinuousSignDays(Long userId) {
        LocalDate today = LocalDate.now();
        String continuousKey = "user:sign:continuous:" + userId;

        // 判断昨天是否签到
        boolean yesterdayChecked = isSignedIn(userId, today.minusDays(1));

        if (yesterdayChecked) {
            // 昨天签到了，连续签到天数+1
            StringRedisTemplate.opsForValue().increment(continuousKey);
        } else {
            // 昨天没签到，重置连续签到天数为1
            StringRedisTemplate.opsForValue().set(continuousKey, "1");
        }
    }


    /**
     * 获取用户当月签到情况
     */
    public List<Integer> getMonthSignData(Long userId, LocalDate date) {
        List<Integer> result = new ArrayList<>();
        String key = buildSignKey(userId, date);
        int dayOfMonth = date.lengthOfMonth(); // 当月总天数

        for (int i = 0; i < dayOfMonth; i++) {
            Boolean isSigned = StringRedisTemplate.opsForValue().getBit(key, i);
            result.add(isSigned != null && isSigned ? 1 : 0);
        }

        return result;
    }

    /**
     * 获取用户连续签到天数
     */
    @Override
    public int getContinuousSignDays(Long userId) {
        String continuousKey = "user:sign:continuous:" + userId;
        String value = StringRedisTemplate.opsForValue().get(continuousKey);
        return value != null ? Integer.parseInt(value) : 0;
    }

    @Override
    public List<Integer> getUserSignInRecord(long userId, Integer year) {
        List<Integer> result = new ArrayList<>();
        LocalDate date = LocalDate.now();
        if (year == null) {
            year = date.getYear();
        }
        int dayOfMonth = date.getDayOfMonth();
        String key = getUserSignInRedisKey(year, userId);
        for (int i = 0; i < dayOfMonth; i++) {
            Boolean isSigned = StringRedisTemplate.opsForValue().getBit(key, i);
            result.add(isSigned != null && isSigned ? 1 : 0);
        }
        return result;
    }
}